package main

import (
	"fmt"
	"io"
	"os"
	"syscall"
	"unsafe"
)

/*
简单理解，mmap 是一种将文件/设备映射到内存的方法，实现文件的磁盘地址和进程虚拟地址空间中的一段虚拟地址的一一映射关系。也就是说，可以在某个进程中通过操作这一段映射的内存，实现对文件的读写等操作。修改了这一段内存的内容，文件对应位置的内容也会同步修改，而读取这一段内存的内容，相当于读取文件对应位置的内容。

mmap 另一个非常重要的特性是：减少内存的拷贝次数。在 linux 系统中，文件的读写操作通常通过 read 和 write 这两个系统调用来实现，这个过程会产生频繁的内存拷贝。比如 read 函数就涉及了 2 次内存拷贝：

操作系统读取磁盘文件到页缓存；
从页缓存将数据拷贝到 read 传递的 buf 中(例如进程中创建的byte数组)。
mmap 只需要一次拷贝。即操作系统读取磁盘文件到页缓存，进程内部直接通过指针方式修改映射的内存。因此 mmap 特别适合读写频繁的场景，既减少了内存拷贝次数，提高效率，又简化了操作。KV数据库 bbolt 就使用了这个方法持久化数据。




mmap() 就是将虚拟地址映射至硬盘上的对象，当CPU首次请求这部分虚拟地址时，发现有效位为0即缺页，触发 页面调度(paging)，将页从磁盘换入内存。如果页被修改则变为脏页，操作系统控制将脏页写回硬盘。

*/

type Student struct {
	Id int
	Age int
}
func main()  {

	fd, err := os.OpenFile("./c.txt",os.O_RDWR|os.O_CREATE,0644)
	defer fd.Close()
	//var pagesperblock int64 = 8192
	//var blocksize int64 = int64(syscall.Getpagesize())*pagesperblock
	sz := 8192
	sizelo := uint32(sz >> 32)
	sizehi := uint32(sz) & 0xffffffff
	/*
	第二个参数是一个指向SECURITY_ATTRIBUTES结构的指针，它指明返回的句柄是否可以被子进程所继承。另外，在SECURITY_ATTRIBUTES结构中，也包括一个安全性描述的子指针，它由WinNT支持它的安全机制
	第三个参数允许指定内存块的访问权限，权限值有PAGE_READONLY、PAGE_READWRITE和PAGE_WRITECOPY，PAGE_WRITECOPY可以在指定页面上拷贝并写访问
	3) 高位文件大小
	亲们， 我想目前我们的机器都是32位的东东， 不可能得到超过32位进程所能寻址的私有32位 地址空间， 一般还是设置0吧， 我没有也不想尝试将它设置超过0的情况。
	4) 低位文件大小
	这个还是可以进行设置的， 不过为了让其他共享用户知道你申请的文件映射的相关信息， 我使用的时候是在获得的 地址空间头部添加一个结构化描述信息， 记录内存映射的大小， 名称等， 这样实际申请的空间就比输入的增加了一个头信息结构大小了， 我认为这样类似BSTR的方式应该是比较合理的。

	4) 高位文件大小dwMaximumSizeHigh
	解释：  32位的机器，设置为0；64位机器按照需求分配空间的大小设置值
	设置值：整型数（表示高于32位的位数）
	5) 低位文件大小dwMaximumSizeLow
	解释：低于32位的值的位数    如果参数（4）（5）都为0，则表示了磁盘文件的实际长度
	6)  共享内存名称lpName

	 HANDLE hFileMap = CreateFileMapping(
	        hFile, // 如果这值为INVALID_HANDLE_VALUE,是合法的，上步一定测试啊
	        NULL, // 默认安全性
	        PAGE_READWRITE, // 可读写
	        0, // 2个32位数示1个64位数，最大文件字节数，
	        // 高字节，文件大小小于4G时，高字节永远为0
	        0,//dwFileSize, // 此为低字节，也就是最主要的参数，如果为0，取文件真实大小
	        NULL);

	PVOID pvFile = MapViewOfFile( //pvFile就是得到的指针，用它来直接操作文件
		hFileMap,
		FILE_MAP_WRITE, // 可写
		0, // 文件指针头位置 高字节
		0, // 文件指针头位置 低字节 必为分配粒度的整倍数,windows的粒度为64K
		0); // 要映射的文件尾，如果为0，则从指针头到真实文件尾
	*/
	hmap, err := syscall.CreateFileMapping(syscall.Handle(fd.Fd()) , nil, syscall.PAGE_READWRITE, sizelo, sizehi, nil)
	defer syscall.CloseHandle(hmap)
	/*
	当用完映像文件后，可以通过调用UnmapViewOfFile函数，释放映像内存并把一些映像数据写入文件（除非它是交换文件），如果想立即把数据写回磁盘文件，那就需要调用FlushViewOfFile()函数把映像内存写入文件。
	offsetHigh 与 offsetLow 都是 0,表示整个文件
	*/
	addr, err := syscall.MapViewOfFile(hmap, syscall.FILE_MAP_WRITE, 0, 0, uintptr(sz))
	fmt.Println(addr,err)
	d := (*[10]Student)(unsafe.Pointer(addr))
	sts := d[:10]
	fmt.Println(sts[0])
	for i:=0;i<10;i++ {
		sts[0].Age=i+10
		sts[0].Id = i
	}
	ssize := unsafe.Sizeof(sts)
	slen := 10*ssize
	fmt.Println("SSIZE:",ssize,slen)
	wf, err := os.OpenFile("./write.txt",os.O_RDWR|os.O_CREATE,0644)
	if err != err{
		fmt.Println(err)
	}
	defer wf.Close()
	num,_ := wf.Seek(0,io.SeekEnd)
	rdata := make([]byte,ssize)
	wf.Read(rdata)
	fmt.Println(rdata)
	fmt.Println("num---------",num)
	wdata := (*[0xffffffff]byte)(unsafe.Pointer(addr))
	fmt.Println(wdata[:slen])
	fmt.Println(wf.WriteAt(wdata[:slen],num))

	defer syscall.UnmapViewOfFile(addr)
	//d := (*[200]byte)(unsafe.Pointer(addr))
	//dd := d[:200]
	//fmt.Println(dd)
	//for i:=0;i<100;i++{
	//	d[i] = '1'
	//}
	return
	//var tmBgn = time.Now()
	//fmt.Println("blocknum=", blocknum)
	//var i int64
	//for i = 0; i < blocknum; i++ {
	//	var datalen int32 = int32(blocksize)
	//	if i == (blocknum - 1) {
	//		datalen = int32(fsize % blocksize)
	//	}
	//	fmt.Println("datalen=", datalen)
	//	//开始读取内存块
	//	var currpos int64 = i * blocksize
	//
	//	var tmplen int = int(datalen)
	//
	//	addr, err := syscall.MapViewOfFile(hmap, syscall.FILE_MAP_READ, 0, uint32(currpos), uintptr(tmplen))
	//	if err != nil {
	//		fmt.Println(err)
	//	}
	//	defer syscall.UnmapViewOfFile(addr)
	//	//把addr变成slice
	//	d := (*[1 << 28]byte)(unsafe.Pointer(addr))
	//	t := d[:tmplen]
	//	fmt.Println(string(t))
	//	//处理数据t
	//}
}

//const maxMapSize = 0x8000000000
//const maxMmapStep = 1 << 30 // 1GB
//func main() {
//	file, err := os.OpenFile("./a.txt", os.O_RDWR|os.O_CREATE, 0644)
//	if err != nil {
//		panic(err)
//	}
//	defer file.Close()
//	stat, err := os.Stat("./a.txt")
//	if err != nil {
//		panic(err)
//	}
//	size, err := mmapSize(int(stat.Size()))
//	if err != nil {
//		panic(err)
//	}
//
//	err = syscall.Ftruncate(int(file.Fd()), int64(size))
//	if err != nil {
//		panic(err)
//	}
//	/*
//	   //err = syscall.Ftruncate(int(file.Fd()), 2)  //第一种改法, truncate() 这里的2，表示会将参数fd指定的文件大小改为参数length指定的大小
//	   //if err != nil {
//	   //    panic(err)
//	   //}
//
//	   // 第二种改法，对文件执行一下write , 或者WriteAt
//	   file.Write(make([]byte, 10)) // 这里的10， 表示文件大小限定为10了， 后面超多10的写不进（打开文件时，字符e没有）。
//	*/
//	b, err := syscall.Mmap(int(file.Fd()), 0, size, syscall.PROT_WRITE|syscall.PROT_READ, syscall.MAP_SHARED)
//	if err != nil {
//		panic(err)
//	}
//	for index, bb := range []byte("Hello world") {
//		b[index] = bb
//	}
//	fmt.Println(b)
//	err = syscall.Munmap(b)
//	if err != nil {
//		panic(err)
//	}
//}
//func mmapSize(size int) (int, error) {
//	// Double the size from 32KB until 1GB.
//	for i := uint(15); i <= 30; i++ {
//		if size <= 1<<i {
//			return 1 << i, nil
//		}
//	}
//	if size > maxMapSize {
//		return 0, fmt.Errorf("mmap too large")
//	}
//	sz := int64(size)
//	// If larger than 1GB then grow by 1GB at a time.
//	if remainder := sz % int64(maxMmapStep); remainder > 0 {
//		sz += int64(maxMmapStep) - remainder
//	}
//
//	pageSize := int64(os.Getpagesize())
//
//	// Ensure that the mmap size is a multiple of the page size.
//	// This should always be true since we're incrementing in MBs.
//	if (sz % pageSize) != 0 {
//		sz = ((sz / pageSize) + 1) * pageSize
//	}
//	if sz > maxMapSize {
//		sz = maxMapSize
//	}
//	return int(sz), nil
//}
